﻿using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Security;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace Utility
{
    public class RSAHelper 
    {
        /// <summary>
        /// RSA C#公钥加密
        /// </summary>
        /// <param name="rawInput">明文</param>
        /// <param name="publicKey">C#公钥秘钥</param>
        /// <returns></returns>
        public static string RsaEncrypt(string rawInput, string publicKey)
        {
            if (string.IsNullOrEmpty(rawInput))
            {
                return string.Empty;
            }

            if (string.IsNullOrWhiteSpace(publicKey))
            {
                throw new ArgumentException("Invalid Public Key");
            }

            using (var rsaProvider = new RSACryptoServiceProvider())
            {
                var inputBytes = Encoding.UTF8.GetBytes(rawInput);//有含义的字符串转化为字节流
                rsaProvider.FromXmlString(publicKey);//载入公钥
                int bufferSize = (rsaProvider.KeySize / 8) - 11;//单块最大长度
                var buffer = new byte[bufferSize];
                using (MemoryStream inputStream = new MemoryStream(inputBytes),
                     outputStream = new MemoryStream())
                {
                    while (true)
                    { //分段加密
                        int readSize = inputStream.Read(buffer, 0, bufferSize);
                        if (readSize <= 0)
                        {
                            break;
                        }

                        var temp = new byte[readSize];
                        Array.Copy(buffer, 0, temp, 0, readSize);
                        var encryptedBytes = rsaProvider.Encrypt(temp, false);
                        outputStream.Write(encryptedBytes, 0, encryptedBytes.Length);
                    }
                    return Convert.ToBase64String(outputStream.ToArray());//转化为字节流方便传输
                }
            }
        }

        /// <summary>
        /// RSA C#私钥解密
        /// </summary>
        /// <param name="encryptedInput">密文</param>
        /// <param name="privateKey">C#私钥</param>
        /// <returns></returns>
        public static string RsaDecrypt(string encryptedInput, string privateKey)
        {
            if (string.IsNullOrEmpty(encryptedInput))
            {
                return string.Empty;
            }

            if (string.IsNullOrWhiteSpace(privateKey))
            {
                throw new ArgumentException("Invalid Private Key");
            }

            using (var rsaProvider = new RSACryptoServiceProvider())
            {
                var inputBytes = Convert.FromBase64String(encryptedInput);
                rsaProvider.FromXmlString(privateKey);
                int bufferSize = rsaProvider.KeySize / 8;
                var buffer = new byte[bufferSize];
                using (MemoryStream inputStream = new MemoryStream(inputBytes),
                     outputStream = new MemoryStream())
                {
                    while (true)
                    {
                        int readSize = inputStream.Read(buffer, 0, bufferSize);
                        if (readSize <= 0)
                        {
                            break;
                        }

                        var temp = new byte[readSize];
                        Array.Copy(buffer, 0, temp, 0, readSize);
                        var rawBytes = rsaProvider.Decrypt(temp, false);
                        outputStream.Write(rawBytes, 0, rawBytes.Length);
                    }
                    return Encoding.UTF8.GetString(outputStream.ToArray());
                }
            }
        }

        /// <summary>
        /// RSA Java公钥解密
        /// </summary>
        /// <param name="strEncryptString">密文</param>
        /// <param name="javaPublicKey">java公钥</param>
        /// <returns></returns>
        public static string RSADecryptByJAVAPublicKey(string strEncryptString, string javaPublicKey)
        {
            byte[] encodedata = null;
            byte[] b2 = new byte[128];
            byte[] dataFromEncrypt = Convert.FromBase64String(strEncryptString);
            encodedata = System.Convert.FromBase64String(javaPublicKey);

            AsymmetricKeyParameter pubKey = PublicKeyFactory.CreateKey(encodedata);
            IBufferedCipher c = CipherUtilities.GetCipher("RSA/ECB/PKCS1Padding");//"RSA");

            using (MemoryStream inputStream = new MemoryStream(dataFromEncrypt),
                    outputStream = new MemoryStream())
            {
                while (true)
                {
                    for (int i = 0; i < 128; i++)
                    {
                        b2[i] = dataFromEncrypt[i];
                    }
                    int readSize = inputStream.Read(b2, 0, b2.Length);
                    if (readSize <= 0)
                    {
                        break;
                    }
                    c.Init(false, pubKey);
                    int blockSize = c.GetBlockSize();
                    c.ProcessBytes(b2);
                    byte[] outbytes = c.DoFinal();
                    outputStream.Write(outbytes, 0, outbytes.Length);
                }
                return Encoding.GetEncoding("GBK").GetString(outputStream.ToArray()).Unicode2String();
            }
        }

        /// <summary>  
        ///   RSA 用私钥给数据进行RSA加密
        /// </summary>  
        /// <param name="xmlPrivateKey">C#私钥格式</param>  
        /// <param name="m_strEncryptString">待加密数据</param>  
        /// <returns>加密后的数据（Base64）</returns>  
        public static string RSAEncryptByPrivateKey(string xmlPrivateKey, string strEncryptString)
        {
            //加载私钥  
            RSACryptoServiceProvider privateRsa = new RSACryptoServiceProvider();
            privateRsa.FromXmlString(xmlPrivateKey);

            //转换密钥  
            AsymmetricCipherKeyPair keyPair = DotNetUtilities.GetKeyPair(privateRsa);

            IBufferedCipher c = CipherUtilities.GetCipher("RSA/ECB/PKCS1Padding");// 参数与Java中加密解密的参数一致       
            //第一个参数为true表示加密，为false表示解密；第二个参数表示密钥  
            c.Init(true, keyPair.Private);

            byte[] DataToEncrypt = Encoding.UTF8.GetBytes(strEncryptString);
            byte[] outBytes = c.DoFinal(DataToEncrypt);//加密  
            string strBase64 = Convert.ToBase64String(outBytes);

            return strBase64;
        }  
    }
}
